#include <Starflub/Starflub.h>
#include <paging.h>

#define INDEX_FROM_BIT(a) (a/(8*4))
#define OFFSET_FROM_BIT(a) (a%(8*4))

//bitset of frames, used or free
u32int *frames;
u32int nframes;

//in kheap.c
extern u32int placement_address;

// set a bit in the frames bitset
static void set_frame(u32int frame_addr) {
	u32int frame = frame_addr/0x1000;
	u32int idx = INDEX_FROM_BIT(frame);
	u32int off = OFFSET_FROM_BIT(frame);
	frames[idx] |= (0x1 << off);
}

// clear a bit in the frames bitset
static void clear_frame(u32int frame_addr) {
	u32int frame = frame_addr/0x1000;
	u32int idx = INDEX_FROM_BIT(frame);
	u32int off = OFFSET_FROM_BIT(frame);
	frames[idx] &= ~(0x1 << off);
}

// test if a bit is set
static u32int test_frame(u32int frame_addr) {
	u32int frame = frame_addr/0x1000;
	u32int idx = INDEX_FROM_BIT(frame);
	u32int off = OFFSET_FROM_BIT(frame);
	return (frames[idx] & (0x1 << off));
}

// find the first free frame.
static u32int first_frame() {
	u32int i, j;
	for(i = 0; i < INDEX_FROM_BIT(nframes); i++) {
		if(frames[i] != 0xFFFFFFFF)
			for(j = 0; j < 32; j++) {
				u32int toTest = 0x1 << j;
				if(!(frames[i]&toTest))
					return i*4*8+j;
			}
	}
}

// shockingly enough, alloc a frame.
void alloc_frame(page_t *page, int is_kernel, int is_writeable) {
	if(page->frame != 0)
		return; // already allocated...
	else {
		u32int idx = first_frame(); //idx is index of first free frame
		if(idx == (u32int)-1)
			SFKernelPanic("No free frames available!", "paging/frames");
		set_frame(idx*0x1000); //the frame is ours!
		page->present = 1;
		page->rw = (is_writeable)?1:0; // should it be writeable?
		page->user = (is_kernel)?0:1; //should it be user-mode?
		page->frame = idx;
	}
}

void free_frame(page_t *page) {
	u32int frame;
	if(!(frame=page->frame))
		return; // page didn't have an alloc'd frame.
	else {
		clear_frame(frame); // frame is free
		page->frame = 0x0; // page has no frame
	}
}

